package ng.max.slideview;

import ohos.agp.components.AttrSet;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.element.Element;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

public class SliderBar extends ComponentContainer {

    private int mMax = 100;
    private int mMin = 0;
    private int mProgress = 0;

    private Element thumb;
    private OnSliderBarChangeListener mOnSliderBarChangeListener;

    private Paint mPaint;
    private Rect bounds;
    private boolean mustInclude = false;

    private SlideView.OnSlideCompleteListener listener;
    private SlideView slideView;

    public SliderBar(Context context) {
        super(context);
        init();
    }

    public SliderBar(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init();
    }

    public SliderBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.GREEN);

        addDrawTask((component, canvas) -> drawThumb(canvas));
        setTouchEventListener((component, event) -> touchEventThumb(event));

        setEstimateSizeListener(new EstimateSizeListener() {
            @Override
            public boolean onEstimateSize(int i, int i1) {
                setEstimatedSize(measureDimension(120, i), measureDimension(100, i1));
                return true;
            }
        });
    }

    private int measureDimension(int defaultSize, int measureSpec) {
        int result = defaultSize;
        int specMode = EstimateSpec.getMode(measureSpec);
        int specSize = EstimateSpec.getSize(measureSpec);
        if (specMode == EstimateSpec.PRECISE) {
            result = specSize;
        } else if (specMode == EstimateSpec.NOT_EXCEED) {
            result = Math.min(defaultSize, specSize);
        }

        return result;
    }

    public boolean isLayoutRtl() {
        return getLayoutDirection() == LayoutDirection.RTL;
    }

    private void drawThumb(Canvas canvas) {
        if (isLayoutRtl()) {
            canvas.translate(getWidth() - getPaddingRight(), getPaddingTop());
            canvas.scale(-1.0f, 1.0f);
        } else {
            canvas.translate(getPaddingLeft(), getPaddingTop());
        }
        bounds = new Rect(getPaddingLeft(), getPaddingTop(), getWidth() / 6, getHeight());

        float sore = (float) getProgress() / getMax();
        float x = (getWidth() - bounds.getWidth()) * sore;

        bounds.offset(getPaddingLeft() + (int) x, getPaddingTop());

        canvas.drawRoundRect(new RectFloat(bounds), bounds.getHeight() / 2f, bounds.getHeight() / 2f, mPaint);

        if (thumb != null) {
            Rect rect = thumb.getBounds();
            if (rect.getWidth() >= bounds.getWidth()) {
                rect.left = bounds.left;
                rect.right = bounds.right;
            }
            if (rect.getHeight() >= bounds.getHeight()) {
                rect.top = bounds.top;
                rect.bottom = bounds.bottom;
            }
            float w = bounds.left + ((float) (bounds.getWidth() - rect.getWidth()) / 2) - 4;
            float h = ((float) (bounds.getHeight() - rect.getHeight()) / 2);
            final int saveCount = canvas.getSaveCount();

            canvas.translate(w, h);
            thumb.drawToCanvas(canvas);
            canvas.restoreToCount(saveCount);
        }
    }

    private boolean isInBounds = false;

    private boolean touchEventThumb(TouchEvent event) {
        if (!isEnabled()) {
            return false;
        }
        switch (event.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                if (mustInclude) {
                    int x = (int) event.getPointerPosition(0).getX();
                    int y = (int) event.getPointerPosition(0).getY();

                    if (isLayoutRtl()) {
                        x = getWidth() - x;
                    }
                    if (bounds.isInclude(x, y)) {
                        isInBounds = true;
                        trackTouchEvent(event);
                        if (mOnSliderBarChangeListener != null) {
                            mOnSliderBarChangeListener.onStartTrackingTouch(SliderBar.this);

                            mOnSliderBarChangeListener.onProgressChanged(SliderBar.this, getProgress(), true);
                        }
                    } else {
                        return true;
                    }
                } else {
                    trackTouchEvent(event);
                    if (mOnSliderBarChangeListener != null) {
                        mOnSliderBarChangeListener.onStartTrackingTouch(SliderBar.this);

                        mOnSliderBarChangeListener.onProgressChanged(SliderBar.this, getProgress(), true);
                    }
                }
                break;
            case TouchEvent.POINT_MOVE:
                if (mustInclude) {
                    if (isInBounds) {
                        trackTouchEvent(event);
                        if (mOnSliderBarChangeListener != null) {

                            mOnSliderBarChangeListener.onProgressChanged(SliderBar.this, getProgress(), true);
                        }
                    } else {
                        return true;
                    }
                } else {
                    trackTouchEvent(event);
                    if (mOnSliderBarChangeListener != null) {
                        mOnSliderBarChangeListener.onProgressChanged(SliderBar.this, getProgress(), true);
                    }
                }
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                if (mOnSliderBarChangeListener != null) {
                    mOnSliderBarChangeListener.onStopTrackingTouch(SliderBar.this);
                }
                if (getProgress() > (getMax() * 0.85)) {
                    if (listener != null) listener.onSlideComplete(slideView);
                }
                isInBounds = false;
                setProgress(0);
                break;
            case TouchEvent.CANCEL:
                if (isInBounds) {
                    isInBounds = false;
                }
                if (getProgress() != 0) {
                    setProgress(0);
                }
                break;
            default:
                break;
        }

        return true;
    }

    void setOnSlideCompleteListenerInternal(SlideView.OnSlideCompleteListener listener, SlideView slideView) {
        this.listener = listener;
        this.slideView = slideView;
    }

    private void trackTouchEvent(TouchEvent event) {
        int x = Math.round(event.getPointerPosition(0).getX());
        int bw = bounds.getWidth() / 2;
        if (x <= bw) {
            x = 0;
        } else if (x > bw && x < getWidth() - bw) {
            x = x - bw;
        } else {
            x = getWidth() - bw;
        }

        final int width = getWidth() - bounds.getWidth();
        final int availableWidth = width - getPaddingLeft() - getPaddingRight();

        final float scale;
        float progress = 0.0f;
        if (isLayoutRtl()) {
            if (x > width - getPaddingRight()) {
                scale = 0.0f;
            } else if (x < getPaddingLeft()) {
                scale = 1.0f;
            } else {
                scale = (availableWidth - x + getPaddingLeft()) / (float) availableWidth;
                progress = 0.6f;
            }
        } else {
            if (x < getPaddingLeft()) {
                scale = 0.0f;
            } else if (x > width - getPaddingRight()) {
                scale = 1.0f;
            } else {
                scale = (x - getPaddingLeft()) / (float) availableWidth;
                progress = 0.6f;
            }
        }
        final int range = getMax() - getMin();
        progress += scale * range;

        setProgress(Math.round(progress));
    }

    public void setOnSliderBarChangeListener(OnSliderBarChangeListener mOnSliderBarChangeListener) {
        this.mOnSliderBarChangeListener = mOnSliderBarChangeListener;
    }

    public Element getThumbElement() {
        return thumb;
    }


    public void setThumbElement(Element thumb) {
        if (thumb == null) {
            throw new NullPointerException("the thumb element not null");
        }
        this.thumb = thumb;
    }

    public void setBackgroundColor(int color) {
        if (mPaint != null) {
            mPaint.setColor(new Color(color));
        }
    }

    public void setBackgroundColor(Color color) {
        if (mPaint != null) {
            mPaint.setColor(color);
        }
    }

    public void setMustInclude(boolean mustInclude) {
        this.mustInclude = mustInclude;
    }

    public void setMin(int min) {
        if (min < 0) {
            this.mMin = 0;
        } else this.mMin = Math.min(min, mMax);
    }

    public void setMax(int max) {
        if (max <= 0) {
            this.mMax = 1;
        } else this.mMax = Math.max(mMin, max);
    }

    public void setProgress(int progress) {
        this.mProgress = Math.min(progress, mMax);
        if (mOnSliderBarChangeListener != null) {
            mOnSliderBarChangeListener.onProgressChanged(SliderBar.this, this.mProgress, true);
        }
        invalidate();
    }

    public int getProgress() {
        return mProgress;
    }

    public int getMax() {
        return mMax;
    }

    public int getMin() {
        return mMin;
    }

    public interface OnSliderBarChangeListener {
        void onProgressChanged(SliderBar sliderBar, int progress, boolean fromUser);

        void onStartTrackingTouch(SliderBar sliderBar);

        void onStopTrackingTouch(SliderBar sliderBar);
    }
}
